//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_TEMPLATE_MATH_H
#define _UXS_TEMPLATE_MATH_H

#pragma once

#include "TemplateUtils.h"

namespace UX_STL
{
namespace CompileTime
{
	using namespace UXTypes;



	//
	// Compile-time Abs
	//

	template <typename T, T X>
	struct Abs
	{
		static const T		value = (X < T(0)) ? -X : X;
	};

	template <uint8 X>
	struct Abs< uint8, X >
	{
		static const uint8	value = X;
	};

	template <uint16 X>
	struct Abs< uint16, X >
	{
		static const uint16	value = X;
	};

	template <uint32 X>
	struct Abs< uint32, X >
	{
		static const uint32	value = X;
	};

	template <uint64 X>
	struct Abs< uint64, X >
	{
		static const uint64	value = X;
	};



	//
	// Sign, SignOrZero
	//
	
	// 1 or -1
	template <typename T, T X>
	struct Sign
	{
		static const int value = ( X >= T(0) ? 1 : -1 );
	};

	// 1 or 0 or -1
	template <typename T, T X>
	struct SignOrZero
	{
		static const int value = ( X > T(0) ) - ( X < T(0) );
	};

	

	//
	// Compile-time Pow
	//

	template <typename T, T X, uint Power>
	struct Pow
	{
		STATIC_ASSERTE( Power < 0xFF );
		static const T	value = T( X * Pow< T, X, Power-1 >::value );
	};
	
	template <typename T, T X>
	struct Pow <T, X, 0>
	{
		static const isize	value = 1;
	};
	


	//
	// Power Of Two (compile time)
	//

	namespace _ctime_hidden_
	{
		template <typename T, T X>
		struct _IsPOT {
			static const bool	value	= ( X != 0 and ( (X & (X - T(1))) == T(0) ) );
		};
		
		template <typename T, T X, uint Bit>
		struct _CmpBits {
			static const uint	value	= (X >> Bit) != 0;
		};

		template <typename T, T X, uint Bit>
		struct __GetPOT {
			static const uint	value	= _CmpBits<T, X, Bit>::value + __GetPOT<T, X, Bit-1>::value;
		};
		
		template <typename T, T X>
		struct __GetPOT <T, X, 0> {
			static const uint	value	= 0;
		};

		template <typename T, T X>
		struct _GetPOT {
			static const uint	value	= __GetPOT<T, X, SizeOf<T>::bits-1 >::value;
		};

	}	// _ctime_hidden_

	template <typename T, T X>
	struct IsPowerOfTwo
	{
		static const bool	value	= _ctime_hidden_::_IsPOT<T, Abs<T,X>::value>::value;
	};

	template <typename T, T X>
	struct MustBePowerOfTwo
	{
		STATIC_ASSERT( (IsPowerOfTwo<T, X>::value), "value must be power of two" );
	};

	template <typename T, T X>
	struct PowerOfTwo
	{
		static const uint	value	=_ctime_hidden_::_GetPOT<T, Abs<T,X>::value>::value;
	};

	template <typename T, T X>
	struct CeilPowerOfTwo
	{
		static const uint	_pot	= PowerOfTwo<T,X>::value + 1;
		static const T		value	= ( T(1) << _pot ) * Sign<T,X>::value;
	};

	template <typename T, T X>
	struct FloorPowerOfTwo
	{
		static const uint	_pot	= PowerOfTwo<T,X>::value;
		static const T		value	= ( T(1) << _pot ) * Sign<T,X>::value;
	};

	template <typename T, T X>
	struct NearPowerOfTwo
	{
		static const T		_x		= Abs<T,X>::value;
		static const uint	_pot	= PowerOfTwo<T,X>::value;
		static const T		_a		= T(1) << (_pot + 1);
		static const T		_b		= T(1) << _pot;
		static const T		_c		= ((_a - _b) >> 1) + _b;
		static const T		value	= ( _x >= _c ? _a : _b ) * Sign<T,X>::value;
	};



	//
	// Greatest common divisor
	//
	
	template <typename T, T Value0, T Value1>
	struct GreatestCommonDivisor
	{
		STATIC_ASSERTE( Value1 != T(0) );
		static const T	value = GreatestCommonDivisor< T, Value1, Value0 % Value1 >::value;
	};

	template <int8 Value0>
	struct GreatestCommonDivisor < int8, Value0, 0 > {
		static const int8	value = Value0;
	};

	template <uint8 Value0>
	struct GreatestCommonDivisor < uint8, Value0, 0 > {
		static const uint8	value = Value0;
	};

	template <int16 Value0>
	struct GreatestCommonDivisor < int16, Value0, 0 > {
		static const int16	value = Value0;
	};

	template <uint16 Value0>
	struct GreatestCommonDivisor < uint16, Value0, 0 > {
		static const uint16	value = Value0;
	};

	template <int32 Value0>
	struct GreatestCommonDivisor < int32, Value0, 0 > {
		static const int32	value = Value0;
	};

	template <uint32 Value0>
	struct GreatestCommonDivisor < uint32, Value0, 0 > {
		static const uint32	value = Value0;
	};

	template <int64 Value0>
	struct GreatestCommonDivisor < int64, Value0, 0 > {
		static const int64	value = Value0;
	};

	template <uint64 Value0>
	struct GreatestCommonDivisor < uint64, Value0, 0 > {
		static const uint64	value = Value0;
	};



	//
	// MinMax
	//

	template <typename T, T a, T b>
	struct MinMax
	{
		static const T	min	= a < b ? a : b;
		static const T	max = a > b ? a : b;
	};

	template <typename T, T a, T b, T c>
	struct MinMax3
	{
		static const T	min = MinMax< T, MinMax< T, a, b >::min, c >::min;
		static const T	max = MinMax< T, MinMax< T, a, b >::max, c >::max;
	};

	template <typename T, T a, T b, T c, T d>
	struct MinMax4
	{
		static const T	min = MinMax< T, MinMax3< T, a, b, c >::min, d >::min;
		static const T	max = MinMax< T, MinMax3< T, a, b, c >::max, d >::max;
	};

	
	//
	// Min
	//

	template <typename T, T a, T b>
	struct Min {
		static const T	value = MinMax< T, a, b >::min;
	};
	
	template <typename T, T a, T b, T c>
	struct Min3 {
		static const T	value = MinMax3< T, a, b, c >::min;
	};

	
	//
	// Max
	//

	template <typename T, T a, T b>
	struct Max {
		static const T	value = MinMax< T, a, b >::max;
	};
	
	template <typename T, T a, T b, T c>
	struct Max3 {
		static const T	value = MinMax3< T, a, b, c >::max;
	};



	//
	// Align
	//

	template <typename T, T Value, uint AlignTo>
	struct Align
	{
		STATIC_ASSERT( (IsPowerOfTwo<uint, AlignTo>::value), "align must be power of two!" );

		static const uint	_ALIGN_POT	= PowerOfTwo<uint, A>::value;
		static const uint	_ALIGN_MASK	= (1 << _ALIGN_POT) - 1;
		static const T		value		= ((Value + _ALIGN_MASK) & ~_ALIGN_MASK);
	};



	//
	// MinLimit, MaxLimit
	//

	template <typename T>
	struct MinLimit
	{
		static const T	value = TypeDescriptor::IsSigned<T>::value ? T( T(1) << (SizeOf<T>::bits-1) ) : T(0);
	};
	
	template <typename T>
	struct MaxLimit
	{
		static const T	value = TypeDescriptor::IsSigned<T>::value ? T( TypeInfo<T>::Similar_Unsigned(-1) & ~MinLimit<T>::value ) : T(-1);
	};



}	// CompileTime
}	// UX_STL

#endif	// _UXS_TEMPLATE_MATH_H